package com.plan.service.uesr.impl;


import com.google.common.collect.Lists;
import com.plan.base.config.property.AppProperty;
import com.plan.base.config.property.WxProperty;
import com.plan.base.constant.BaseCode;
import com.plan.base.entity.Result;
import com.plan.base.util.DateUtil;
import com.plan.base.util.HttpUtil;
import com.plan.controller.user.request.MiniProgramLoginRequest;
import com.plan.controller.user.response.LoginResponse;
import com.plan.controller.user.response.UserInitData;
import com.plan.dao.mapper.user.PackageMapper;
import com.plan.dao.mapper.user.UserMapper;
import com.plan.dao.service.user.UserTransactionService;
import com.plan.entity.card.*;
import com.plan.entity.third.wx.response.MiniProgramAccountInfo;
import com.plan.entity.third.wx.response.WeiXinMiniProgmaLoginResponse;
import com.plan.entity.user.Package;
import com.plan.entity.user.User;
import com.plan.entity.user.UserPackage;
import com.plan.enums.*;
import com.plan.service.uesr.AuthManager;
import com.plan.service.uesr.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private WxProperty wxProperty;
    @Resource
    private AppProperty appProperty;
    @Resource
    private UserTransactionService userTransactionService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private PackageMapper packageMapper;
    @Resource
    private AuthManager authManager;

    @Override
    public User getUserByAccount(String account, int type) {
        if (type == AccountTypeEnum.WX.getType()) {
            return userMapper.selectOneByOpenIdWx(account);
        } else if (type == AccountTypeEnum.ALIPAY.getType()) {
            return userMapper.selectOneByOpenIdAlipay(account);
        } else if (type == AccountTypeEnum.PHONE.getType()) {
            return userMapper.selectOneByOpenIdPhone(account);
        }
        log.warn("未知账号类型，accountType={} account={}", type, account);
        return null;
    }

    @Override
    public Result<LoginResponse> wxLogin(MiniProgramLoginRequest request) {
        try {
            // 获取openId和sessionKey
            Result<MiniProgramAccountInfo> openInfoResult = this.getWxMiniProgramOpenId(request);
            if (!openInfoResult.isOk()) {
                return Result.fail(openInfoResult.getCode());
            }
            MiniProgramAccountInfo info = openInfoResult.getData();


            User user = this.getUserByAccount(info.getOpenId(), AccountTypeEnum.WX.getType());
            if (user == null) {
                //小程序账户不存在则新建
                user = new User();
                user.setName("爱学习的小可爱");
                user.setAccountType(AccountTypeEnum.WX.getType());
                user.setLevel(UserLevelEnum.NORMAL.getLevel());
                user.setOpenIdWx(info.getOpenId());
                user.setLoginCount(1);
                user.setLastLoginTime(new Date());
                user.setDelFlag(DelFlagEnum.NORMAL.getType());
                // 默认1，付费购买
                user.setCardNum(1);

                this.handleLoginCommon(user);
            } else {
                user.setLastLoginTime(new Date());
                user.setLoginCount(user.getLoginCount() + 1);
                User updateUser = new User();
                updateUser.setId(user.getId());
                updateUser.setLoginCount(user.getLoginCount() + 1);
                updateUser.setLastLoginTime(new Date());
                userTransactionService.update(user);
            }

            String token = authManager.creatToken(user);
            LoginResponse res = new LoginResponse();
            res.setUser(user);
            res.setToken(token);
            return Result.ok(res);
        } catch (Exception e) {
            log.error("wxLogin error", e);
            return Result.fail(BaseCode.SYS_ERROR);
        }
    }

    private void handleLoginCommon(User user) {
        // 查回资源包，然后每个资源包试用1周
        List<UserPackage> userPackages = null;
        List<Package> packages = packageMapper.selectAllPackages();
        if (!CollectionUtils.isEmpty(packages)) {
            userPackages = new ArrayList<>(packages.size());
            for (Package item : packages) {
                UserPackage userPackage = new UserPackage();
                userPackage.setPackageId(item.getId());
                userPackage.setExpireTime(DateUtil.addDays(new Date(), appProperty.getFreeDay()));
                userPackages.add(userPackage);
            }
        }
        UserInitData userInitData = this.buildInitData();
        userTransactionService.registy(user, userInitData, userPackages);
    }

    private UserInitData buildInitData() {
        // 注册时新建一个团队，即家庭
        Team team = new Team();
        team.setName("我的家庭");
        team.setCardNum(1);
        team.setCreatorId(0L);
        team.setUpdaterId(0L);
        team.setVersion(0);
        team.setTaskRepositoryMaxNum(0);
        team.setType(TeamEnum.FAMILY.getType());
        team.setDelFlag(DelFlagEnum.NORMAL.getType());

        // 生成一个示例积分卡
        Card card = new Card();
        card.setGender(1);
        card.setBirthday(new Date());
        card.setVersion(0);
        card.setName("薛霸(长按删示例)");
        card.setTotalPoint(100);
        card.setRemainPoint(100);
        card.setDelFlag(DelFlagEnum.NORMAL.getType());

        // 生成几个今日任务
        DayPlan d = new DayPlan();
        d.setYearMonthDay(DateUtil.getYearMonthDayFromDate(new Date()));
        d.setName("自己叠被子");
        d.setAwardPoint(3);
        d.setIntoMemory(0);
        d.setIntoTaskRepository(0);
        d.setDelFlag(DelFlagEnum.NORMAL.getType());
        d.setStatus(DayPlanStatusEnum.COMPLETED.getStatus());
        // 已完成要生成积分日志
        PointLog p = new PointLog();
        p.setPoint(d.getAwardPoint());
        p.setEventName(d.getName());
        p.setCreateTime(new Date());
        p.setDelFlag(DelFlagEnum.NORMAL.getType());
        p.setPointType(PointTypeEnum.COMPLETE_PLAN.getType());

        DayPlan d2 = new DayPlan();
        d2.setYearMonthDay(DateUtil.getYearMonthDayFromDate(new Date()));
        d2.setName("帮妈妈做家务");
        d2.setAwardPoint(5);
        d2.setIntoMemory(0);
        d2.setIntoTaskRepository(0);
        d2.setDelFlag(DelFlagEnum.NORMAL.getType());
        d2.setStatus(DayPlanStatusEnum.STARTED.getStatus());

        DayPlan d3 = new DayPlan();
        d3.setYearMonthDay(DateUtil.getYearMonthDayFromDate(new Date()));
        d3.setName("自觉高效完成家庭作业");
        d3.setAwardPoint(5);
        d3.setIntoMemory(0);
        d3.setIntoTaskRepository(0);
        d3.setDelFlag(DelFlagEnum.NORMAL.getType());
        d3.setStatus(DayPlanStatusEnum.NEW.getStatus());

        DayPlan d4 = new DayPlan();
        d4.setYearMonthDay(DateUtil.getYearMonthDayFromDate(new Date()));
        d4.setName("考试95分以上");
        d4.setAwardPoint(10);
        d4.setIntoMemory(0);
        d4.setIntoTaskRepository(0);
        d4.setDelFlag(DelFlagEnum.NORMAL.getType());
        d4.setStatus(DayPlanStatusEnum.NEW.getStatus());

        // 再生成2个昨天的历史任务
        DayPlan d6 = new DayPlan();
        d6.setYearMonthDay(DateUtil.getYearMonthDayFromDate(DateUtil.addDays(new Date(), -1)));
        d6.setName("英语分级15-20课");
        d6.setAwardPoint(80);
        d6.setIntoMemory(0);
        d6.setIntoTaskRepository(0);
        d6.setDelFlag(DelFlagEnum.NORMAL.getType());
        d6.setStatus(DayPlanStatusEnum.COMPLETED.getStatus());
        PointLog p6 = new PointLog();
        p6.setPoint(d6.getAwardPoint());
        p6.setEventName(d6.getName());
        p6.setCreateTime(DateUtil.addDays(new Date(), -1));
        p6.setDelFlag(DelFlagEnum.NORMAL.getType());
        p6.setPointType(PointTypeEnum.COMPLETE_PLAN.getType());

        DayPlan d7 = new DayPlan();
        d7.setYearMonthDay(DateUtil.getYearMonthDayFromDate(DateUtil.addDays(new Date(), -1)));
        d7.setName("10-15课自然拼读");
        d7.setAwardPoint(10);
        d7.setIntoMemory(0);
        d7.setIntoTaskRepository(0);
        d7.setDelFlag(DelFlagEnum.NORMAL.getType());
        d7.setStatus(DayPlanStatusEnum.COMPLETED.getStatus());
        PointLog p7 = new PointLog();
        p7.setPoint(d7.getAwardPoint());
        p7.setEventName(d7.getName());
        p7.setCreateTime(DateUtil.addDays(new Date(), -1));
        p7.setDelFlag(DelFlagEnum.NORMAL.getType());
        p7.setPointType(PointTypeEnum.COMPLETE_PLAN.getType());


        DayPlanStat s = new DayPlanStat();
        s.setYearMontDay(DateUtil.getYearMonthDayFromDate(DateUtil.addDays(new Date(), -1)));
        s.setGetPoint(90);
        s.setTaskNum(2);
        DayPlanStat yesterdayStat = new DayPlanStat();
        yesterdayStat.setYearMontDay(DateUtil.getYearMonthDayFromDate(DateUtil.addDays(new Date(), -1)));
        yesterdayStat.setGetPoint(10);
        yesterdayStat.setTaskNum(4);

        UserInitData initData = new UserInitData();
        initData.setCard(card);
        initData.setTeam(team);
        initData.setDayPlanStats(Lists.newArrayList(s, yesterdayStat));
        initData.setPointLogs(Lists.newArrayList(p, p6, p7));
        initData.setDayPlanList(Lists.newArrayList(d, d2, d3, d4, d6, d7));

        return initData;
    }


    /**
     * 获取微信小程序openId
     */
    private Result<MiniProgramAccountInfo> getWxMiniProgramOpenId(MiniProgramLoginRequest request) {
        //1.取openid
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
        String weiXinUrl = null;
        try {
            weiXinUrl = String.format(url, wxProperty.getAppId(), wxProperty.getSecret(), URLEncoder.encode(request.getLoginCode(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            log.error("微信登录时地址编辑错误", e);
            return Result.fail(BaseCode.TENCENT_NET_ERROR);
        }

        WeiXinMiniProgmaLoginResponse response2 = HttpUtil.sendHttpGetRequest(weiXinUrl, WeiXinMiniProgmaLoginResponse.class, null);
        if (response2 == null || response2.getErrcode() != null) {
            if (response2 == null) {
                log.error("微信登录时发生网络错误");
                return Result.fail(BaseCode.TENCENT_NET_ERROR);
            } else {
                log.error("微信登录时发生错误,errCode={},errMsg={}", response2.getErrcode(), response2.getErrmsg());
            }
            return Result.fail(BaseCode.TENCENT_SERVER_ERROR);
        }

        MiniProgramAccountInfo info = new MiniProgramAccountInfo();
        info.setOpenId(response2.getOpenId());
        info.setSessionKey(response2.getSessionKey());
        return Result.ok(info);
    }


}
